package org.hubwork.base.common;

import java.util.HashMap;
import java.util.LinkedList;

import org.springframework.beans.BeanUtils;


public abstract class ThreadContext
{
  private static ThreadLocal<LinkedList<HashMap<String, Object>>> request = new ThreadLocal<LinkedList<HashMap<String, Object>>> ();
  private static ThreadLocal<HashMap<String, Object>> contexts = new ThreadLocal<HashMap<String, Object>> ();
  private static ThreadLocal<Boolean> authFlag = new ThreadLocal<Boolean>();
  private static ThreadLocal<Integer> requestCount = new ThreadLocal<Integer>()
  {
    @Override
    protected Integer initialValue()
    {
        return new Integer(0);
    }
  };
  private static ThreadLocal<HashMap<String, Object>> requestLocalData = new ThreadLocal<HashMap<String, Object>> ()
  {
    @Override
    protected HashMap<String, Object> initialValue()
    {
        return new HashMap<String, Object>();
    }
  };

  public static HashMap<String, Object> push ()
  {
    HashMap<String, Object> context = new HashMap<String, Object> ();
    contexts.set (context);
    getList ().addLast (context);
    return context;
  }
  public static HashMap<String, Object> pushRequest ()
  {
    requestCount.set (requestCount.get()+1);
    return push();
  }
  public static HashMap<String, Object> popRequest ()
  {
    requestCount.set (requestCount.get()-1);
    if (requestCount.get ()== 0)
    {
      destroyRequestLocalData();
    }
    return pop ();
  }
  public static HashMap<String, Object> pop ()
  {
    LinkedList<HashMap<String, Object>> list = getList ();
    HashMap<String, Object> context = list.removeLast ();
    if (list.size () > 0)
    {
      contexts.set (list.getLast ());
    }
    else
    {
      HashMap<String, Object> root = new HashMap<String, Object> ();
      list.addLast (root);
      contexts.set (root);
    }
    return context;
  }
  public static boolean contains (String name)
  {
    HashMap<String, Object> context = contexts.get ();
    if (context == null) return false;
    else return context.containsKey (name);
  }
  
  @SuppressWarnings("unchecked")
public static <T> T get (String name, Class<? extends T> clazz)
  {
    T out = null;
    
    HashMap<String, Object> map = contexts.get ();
    
    if (map == null)
    {
      map = push ();
    }
    
    out = (T) map.get (name);
    
    if (out == null && clazz != null)
    {
    	out = (T) BeanUtils.instantiateClass (clazz);	
  
      map.put (name, out);
    }
    
    return out;
  }
  
  public static <T> T set (String name, T value)
  {
    HashMap<String, Object> map = contexts.get ();
    
    if (map == null)
    {
      map = push ();
    }
    
    map.put (name, value);
        
    return value;
  }
  
  public static void remove (String name)
  {
    HashMap<String, Object> map = contexts.get ();
    
    if (map != null)
    {
      map.remove (name);
    }
    
  }

  private static LinkedList<HashMap<String, Object>> getList ()
  {
    LinkedList<HashMap<String, Object>> out = request.get ();
    if (out == null)
    {
      out = new LinkedList<HashMap<String, Object>> ();
      out.addLast (new HashMap<String, Object> ());
      request.set (out);
    }
    return out;
  }
  
  public static Boolean getAuthFlag ()
  {
    return authFlag.get ();
  }
  public static void setAuthFlag (Boolean value)
  {
    authFlag.set (value);
  }
  
  @SuppressWarnings("unchecked")
public static <T> T getRequestLocalData (String name)
  {
    T out = null;
    
    HashMap<String, Object> map = requestLocalData.get ();
    out = (T) map.get (name);
    return out;
  }
  
  public static <T> void setRequestLocalData (String name, T value)
  {
    HashMap<String, Object> map = requestLocalData.get ();    
    map.put (name, value);
  }        
  
  @SuppressWarnings("unchecked")
public static <T> T removeRequestLocalData (String name)
  {
    HashMap<String, Object> map = requestLocalData.get ();    
    return (T) map.remove (name);
  }
  
  public static void destroyRequestLocalData ()
  {
    HashMap<String, Object> map = requestLocalData.get ();    
    map.clear ();
  }
}
